package com.bsy.common.viewmodel

import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bsy.common.manager.DialogManager
import com.bsy.framework.ext.data
import com.bsy.framework.log.LogUtil
import com.bsy.framework.manager.ActivityManager
import com.bsy.framework.toast.Toaster
import com.bsy.network.callback.IApiErrorCallback
import com.bsy.network.error.ApiException
import com.bsy.network.error.ERROR
import com.bsy.network.error.ExceptionHandler
import com.bsy.network.flow.requestFlow
import com.bsy.network.response.BaseResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout

/**
 * @author Sincerity
 * @date   2023/2/24 13:03
 * @desc   viewModel基类
 */
open class BaseViewModel : ViewModel() {
    /**
     * 运行在主线程中，可直接调用  适用于在 ViewModel 中执行普通的异步网络请求，
     * 并且不需要直接处理请求结果的场景。例如，执行一个简单的网络请求获取数据，然后更新 UI
     * @param errorBlock 错误回调
     * @param responseBlock 请求函数
     */
    fun launchUI(
        responseBlock: suspend () -> Unit,
        errorBlock: ((Int?, String?) -> Unit) = { _, _ -> }
    ) {
        viewModelScope.launch(Dispatchers.Main) {
            safeApiCall(errorBlock = errorBlock, responseBlock)
        }
    }

    /**
     * 需要运行在协程作用域中
     * @param errorBlock 错误回调
     * @param responseBlock 请求函数
     */
    private suspend fun <T> safeApiCall(
        errorBlock: suspend (Int?, String?) -> Unit,
        responseBlock: suspend () -> T?
    ): T? {
        try {
            return responseBlock()
        } catch (e: Exception) {
            val exception = ExceptionHandler.handleException(e)
            if (isNeedLoginCode(exception.errCode)) {
                Toaster.show(exception.errMsg)
                //todo 去登录页
            } else if (exception.errCode == ERROR.JOB_CANCEL.code) {
                LogUtil.e("${e.message}")
            } else {
                Toaster.show(exception.errMsg)
                errorBlock(exception.errCode, exception.errMsg)
            }
        }
        return null
    }

    /**
     * 不依赖BaseRepository，运行在主线程中，可直接调用 适用于在 ViewModel 中执行需要获取请求结果的异步网络请求场景。
     * 例如，执行一个网络请求获取数据，并且需要根据请求结果进行相应的处理，如更新 UI 或者处理错误。
     * @param errorCall 错误回调
     * @param responseBlock 请求函数
     * @param successBlock 请求回调
     */
    fun <T> launchUIWithResult(
        responseBlock: suspend () -> BaseResponse<T>?,
        errorCall: IApiErrorCallback?,
        successBlock: (T?) -> Unit
    ) {
        viewModelScope.launch(Dispatchers.Main) {
            val result = safeApiCallWithResult(errorCall = errorCall, responseBlock)
            successBlock(result)
        }
    }

    /**
     * 不依赖BaseRepository，需要在作用域中运行
     * @param errorCall 错误回调
     * @param responseBlock 请求函数
     */
    private suspend fun <T> safeApiCallWithResult(
        errorCall: IApiErrorCallback?,
        responseBlock: suspend () -> BaseResponse<T>?
    ): T? {
        try {
            val response = withContext(Dispatchers.IO) {
                withTimeout(10 * 1000) {
                    responseBlock()
                }
            } ?: return null

            if (!response.isSuccess()) {
                throw ApiException(response.code, response.msg)
            }
            return response.data
        } catch (e: Exception) {
            e.printStackTrace()
            LogUtil.e(e)
            val exception = ExceptionHandler.handleException(e)
            if (isNeedLoginCode(exception.errCode) && ActivityManager.top().componentName?.className != "com.bsy.login.ui.LoginMainActivity") {
                errorCall?.onLoginFail(exception.errCode, exception.errMsg)
            } else if (exception.errCode == ERROR.JOB_CANCEL.code) {
                LogUtil.e("${e.message}")
            } else {
                errorCall?.onError(exception.errCode, exception.errMsg)
            }
        }
        return null
    }

    /**
     * flow 运行在主线程中，可直接调用 适用于在 ViewModel 中执行需要使用流式 API 的网络请求场景。
     * 例如，执行一个长时间运行的网络请求或者是多个连续的网络请求，并且希望能够在请求过程中处理数据流。
     * @param errorCall 错误回调
     * @param requestCall 请求函数
     * @param showLoading 是否展示加载框
     * @param successBlock 请求结果
     */
    fun <T> launchFlow(
        errorCall: IApiErrorCallback? = null,
        requestCall: suspend () -> BaseResponse<T>?,
        showLoading: ((Boolean) -> Unit)? = null,
        successBlock: (T?) -> Unit
    ) {
        viewModelScope.launch(Dispatchers.Main) {
            val data = requestFlow(errorBlock = { code, error ->
                if (isNeedLoginCode(code.data())) {
                    errorCall?.onLoginFail(code, error)
                } else {
                    errorCall?.onError(code, error)
                }
            }, requestCall, showLoading)
            successBlock(data)
        }
    }

    private suspend fun <T> requestMultipleFlows(
        errorBlock: ((Int?, String?) -> Unit)? = null,
        requestCalls: List<suspend () -> BaseResponse<T>?>,
        showLoading: ((Boolean) -> Unit)? = null
    ): List<T?> {
        return coroutineScope {
            // 并行执行所有请求
            val response = requestCalls.map { requestCall ->
                async {
                    requestFlow(errorBlock, requestCall, showLoading)
                }
            }
            // 等待所有请求完成并收集结果
            response.awaitAll()
        }
    }

    /**
     * 并行执行多个网络请求
     * @param errorCall 错误回调
     * @param requestCalls 执行的请求列表
     * @param showLoading 开启和关闭加载框
     * @return List<T?>
     */
    fun <T> launchMultipleFlows(
        errorCall: IApiErrorCallback? = null,
        requestCalls: List<suspend () -> BaseResponse<T>?>,
        showLoading: ((Boolean) -> Unit)? = null,
        successBlock: (List<T?>) -> Unit
    ) {
        viewModelScope.launch(Dispatchers.Main) {
            val data = requestMultipleFlows(errorBlock = { code, error ->
                if (isNeedLoginCode(code.data())) {
                    errorCall?.onLoginFail(code, error)
                } else {
                    errorCall?.onError(code, error)
                }
            }, requestCalls, showLoading)
            successBlock(data)
        }
    }

    private val loginArr = intArrayOf(
        ERROR.UNLOGIN.code,
        ERROR.USER_UNUSUAL.code,
        ERROR.LOGIN_TOKEN_ERROR.code,
        ERROR.LOGIN_EXPIRED.code,
        ERROR.LOGIN_REMOUNT.code,
        ERROR.USER_CLOSE.code,
        ERROR.USER_PWD_MISMATCH.code,
        ERROR.USER_LOCK.code
    )

    /**
     * 当前状态码是否需要强制回到登录页
     */
    private fun isNeedLoginCode(code: Int): Boolean {
        return loginArr.indexOf(code) != -1
    }
}